package protocol.hdlc;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

import protocol.arq.ARQSender;
import protocol.arq.gobackN.GoBackNSender;
import utils.advancedSocket.AdvancedSocket;
import utils.advancedSocket.AdvancedSocketAdapter;
import utils.advancedSocket.AdvancedSocketCloseEvent;

/**
 * Envoie les donnees d'un {@link BufferedReader} ou d'un fichier selon un
 * protocole d'envoi déterminer dans le constructeur.
 * 
 * @author Bertholet Damien & Daoust Julien
 */
public class Sender extends HDLCProtocol {

	/**
	 * Lance l'envoie du fichier sur le port souhaite.
	 * 
	 * @param args
	 *            Prend quatre arguments : <Nom_Machine> <Numero_Port>
	 *            <Nom_fichier> <0>
	 */
	public static void main(String[] args) {
		String utilisation =
		        "Utilisation : java Sender <Nom_Machine> <Numero_Port> <Nom_fichier> <0>";
		if(args.length != 4)
			throw new IllegalArgumentException(
			        "Number of arguments who are incorrects. " + utilisation);

		String machineName = args[0];

		int port;
		try {
			port = Integer.parseInt(args[1]);
		} catch (NumberFormatException e) {
			throw new IllegalArgumentException(
			        "Port's number must be an integer. " + utilisation);
		}

		String fileName = args[2];
		File file = new File(fileName);
		if(!file.exists())
			throw new IllegalArgumentException("The file doesn't exist. " +
			        utilisation);
		if(file.isDirectory())
			throw new IllegalArgumentException("Is not a file. " + utilisation);

		int sendingMethod;
		try {
			sendingMethod = Integer.parseInt(args[3]);
		} catch (NumberFormatException e) {
			throw new IllegalArgumentException(
			        "The sending method must be an integer. " + utilisation);
		}

		try {
			new Sender(machineName, port, sendingMethod, file);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Constructeur.
	 * 
	 * @param machineName
	 *            Le nom de la machine.
	 * @param port
	 *            Le port pour communiquer.
	 * @param sendingMethod
	 *            La methode d'envoie des donnees (0 pour Go-Back-N, ...).
	 * @param file
	 *            Le fichier a envoyer.
	 * @throws Exception
	 *             Si une erreur survient durant le processus.
	 */
	@SuppressWarnings("resource")
	public Sender(String machineName, int port, int sendingMethod, File file)
	        throws Exception {
		this(machineName, port, sendingMethod, new BufferedReader(
		        new InputStreamReader(new FileInputStream(file),
		                HDLCProtocol.charset)));
	}

	/**
	 * Constructeur.
	 * 
	 * @param machineName
	 *            Le nom de la machine.
	 * @param sendingMethod
	 *            La methode d'envoie des donnees (0 pour Go-Back-N, ...).
	 * @param port
	 *            Le port pour communiquer.
	 * @param reader
	 *            Les donnees a envoyer.
	 * @throws Exception
	 *             Si une erreur survient durant le processus.
	 */
	@SuppressWarnings("resource")
	public Sender(String machineName, int port, int sendingMethod,
	        final BufferedReader reader) throws Exception {
		if(HDLCProtocol.VERBOSE) {
			System.out.println("Sender");
			System.out.println("Machine Name :" + machineName);
			System.out.println("Port :" + port);
			System.out.println("SendingMethod :" + sendingMethod + "\n");
		}

		AdvancedSocket advancedSocket =
		        new AdvancedSocket(new Socket(machineName, port));

		advancedSocket.addAdvancedSocketListener(new AdvancedSocketAdapter() {

			@Override
			public void onCloseConnection(AdvancedSocketCloseEvent event) {
				try {
					reader.close();
				} catch (IOException e) {
					// Do nothing
				}
			}
		});

		ARQSender sender =
		        Sender.getSendingMethod(sendingMethod, advancedSocket, reader);
		sender.startConnection();
	}

	/**
	 * Retourne un object {@link ARQSender} qui represente la methode d'envoie
	 * choisit.
	 * 
	 * @param sendingMethod
	 *            La methode d'envoie des donnees (0 pour Go-Back-N, ...).
	 * @param advancedSocket
	 *            L'{@link AdvancedSocket}.
	 * @param reader
	 *            Le {@link BufferedReader} des donnees.
	 * @return L'{@link ARQSender}.
	 * @throws Exception
	 *             Si une erreur survient durant la creation de la methode.
	 */
	private static ARQSender getSendingMethod(int sendingMethod,
	        AdvancedSocket advancedSocket, BufferedReader reader)
	        throws Exception {
		switch(sendingMethod) {
		case 0:
			TrameBuilder trameBuilder =
			        new TrameBuilder(reader,
			                HDLCProtocol.calculWindowSize(sendingMethod));
			return new GoBackNSender(advancedSocket, trameBuilder);
		default:
			throw new IllegalArgumentException("Sending method unknown :" +
			        sendingMethod);
		}
	}
}